﻿//-----------------------------------------------------------------------
// <copyright>
//     Copyright (c) Artur Mustafin. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

using System;
using System.Collections.Generic;
using Expressions.Compiler;
using Math.Compiler;
using Math.Compiler.Expressions;

namespace Math.Compiler.Classes
{
    internal class Instructions
    {
        private readonly Dictionary<int, Instruction> _instructions;

        internal Instructions()
        {
            Externals.ImportExternals();
            _instructions = new Dictionary<int, Instruction>();
        }

        internal void Update(Variable variable, string text, MathExpression expression)
        {
            Remove(variable);
            variable.Unsubscribe();
            variable.Visit();
            variable.MathExpression = expression;
            variable.Text = text;
            variable.Subscribe();
            variable.Visit();
            Add(variable);
        }

        internal void Load(IEnumerable<string> lines)
        {
            if (_instructions.Count > 0)
            {
                foreach (Instruction instruction in _instructions.Values)
                {
                    instruction.Variable.Clear();
                }
            }
            _instructions.Clear();

            Nonterminal<char> grammar = NonterminalsDefinition.assigment_expression;
            Expression<char, MathExpression> expression = ExpressionsDefinition.assigment_expression;

            grammar.Initialize();
            grammar.Validate();

            foreach (string line in lines)
            {
                if (line == null || string.IsNullOrEmpty(line) || string.IsNullOrWhiteSpace(line))
                {
                    throw new NotSupportedException("Initialization of the blank input line is not supported");
                }

                Parser<char> input = line.ToCharArray();
                if (!grammar.Parse(input))
                {
#if TRACE
                    Parser<char>.ClearTraceLog();
#endif
                    throw new NotSupportedException("Initialization of the invalid assigment expression is not supported");
                }
#if TRACE
                Parser<char>.WriteTraceLog();
#endif
                Assignment instruction = expression[input.Tree] as Assignment;
                if (instruction == null)
                {
                    throw new NotSupportedException("Assigment of the null instruction is not supported");
                }

                VariableReference reference = instruction.CellReference;
                if (reference == null)
                {
                    throw new NotSupportedException("Assigment of the null instruction reference is not supported");
                }

                MathExpression instructionExpression = instruction.MathExpression;
                if (instructionExpression == null)
                {
                    throw new NotSupportedException("Assigment of the null instruction expression is not supported");
                }
                Variable current = reference.Variable;

                Remove(current);
                current.Text = instruction.Text;
                current.MathExpression = instruction.MathExpression;
                Add(current);
            }

            List<Instruction> instuctions = new List<Instruction>(_instructions.Values);

            foreach (Instruction instruction in instuctions)
            {
                Variable variable = instruction.Variable;
                MathExpression eval = instruction.Expression;
                if (!variable.Validate(eval))
                {
                    throw new NotSupportedException("Initialization of the circle referenced instruction is not supported");
                }
                variable.Subscribe();
            }
            foreach (Instruction instruction in instuctions)
            {
                instruction.Variable.Visit();
            }
        }

        internal IEnumerable<string> Save()
        {
            List<string> lines = new List<string>();
            if (_instructions.Count > 0)
            {
                foreach (Instruction instruction in _instructions.Values)
                {
                    lines.Add(string.Format("{0}={1}", instruction.Variable.Name, instruction.Variable.Text));
                }
            }
            return lines;
        }

        /// <summary>
        /// Adds the specified variable.
        /// </summary>
        /// <param name="variable">The variable.</param>
        public void Add(Variable variable)
        {
            if (!_instructions.ContainsKey(variable.Id))
            {
                _instructions.Add(variable.Id, new Instruction() { Variable = variable, Expression = variable.MathExpression });
            }
        }

        /// <summary>
        /// Removes the specified variable.
        /// </summary>
        /// <param name="variable">The variable.</param>
        public void Remove(Variable variable)
        {
            if (_instructions.ContainsKey(variable.Id))
            {
                _instructions.Remove(variable.Id);
            }
        }

        /// <summary>
        /// Gets the variables.
        /// </summary>
        /// <value>The variables.</value>
        public IEnumerable<Variable> Variables
        {
            get
            {
                List<Variable> variables = new List<Variable>();
                foreach (Instruction instruction in _instructions.Values)
                {
                    variables.Add(instruction.Variable);
                }
                return variables;
            }
        }
    }
}
